#include "AviMovie.h"

unsigned AviMovie::m_suObjectCounter=0;

AviMovie::AviMovie(const char* filename){
	m_suObjectCounter++;
	m_bOpenMode=Read;
	m_uCurrentFrameNum=0;

	if(m_suObjectCounter==1)
		AVIFileInit();

	if(AVIFileOpen(&m_aviFile,filename,OF_SHARE_DENY_WRITE,NULL)!=AVIERR_OK){
		//error handling
		exit(-1);
	}

	readHeader();
	m_uPlaneSize = m_uWidth*m_uHeight;
	m_uFrameSize = m_uPlaneSize*3;
	m_pFrameContainer = new byte[m_uFrameSize];
	/*AVIFileInfo(m_aviFile, &m_aviFileInfo, 108);
	m_uWidth = m_aviFileInfo.dwWidth;
	m_uHeight = m_aviFileInfo.dwHeight;
	m_uPlaneSize = m_uWidth*m_uHeight;
	m_uFrameSize = m_uPlaneSize*3;
	m_uScale = m_aviFileInfo.dwScale;
	m_uRate = m_aviFileInfo.dwRate;
	m_uLength = m_aviFileInfo.dwLength;
	*/
}

AviMovie::AviMovie(const char* szFileName,
					unsigned uWidth,
					unsigned uHeight,
					unsigned uScale,
					unsigned uRate,
					unsigned uLength
					){

	m_suObjectCounter++;
	m_bOpenMode=Write;
	m_uCurrentFrameNum=0;
	
	if(1==m_suObjectCounter)
		AVIFileInit();

	if(AVIFileOpen(&m_aviFile,szFileName,OF_WRITE|OF_CREATE,NULL)!=AVIERR_OK){
		//error handling
		exit(-1);

	// OF_CREATE 
	// Creates a new file. If the file already exists, it is truncated to zero length.
	}	
	writeHeader(uWidth,uHeight,uScale,uRate,uLength);
	m_uPlaneSize = m_uWidth*m_uHeight;
	m_uFrameSize = m_uPlaneSize*3;
	m_pFrameContainer = new byte[m_uFrameSize];
}	


AviMovie::~AviMovie(void){
	if(Write==m_bOpenMode){
		if(m_VideoStream)
			AVIStreamRelease(m_VideoStream); 
	}
	else{	
	}
	delete []m_pFrameContainer;
	AVIFileRelease(m_aviFile);
	m_suObjectCounter--;
	if(0==m_suObjectCounter)
		AVIFileExit();
}

void AviMovie::readHeader(){	
	LONG VideoStreamSize;
	AVISTREAMINFO VideoStreamInfo;
	BITMAPINFOHEADER bmpih;

	if(AVIFileGetStream(m_aviFile,&m_VideoStream,streamtypeVIDEO,0)!=AVIERR_OK){
		//error handling
		exit(-1);
	}
	if(AVIStreamInfo(m_VideoStream,&VideoStreamInfo,sizeof(VideoStreamInfo))!=AVIERR_OK){
		//error handling
		exit(-1);
	}
	
	AVIStreamFormatSize(m_VideoStream,0,&VideoStreamSize);
	//error handling
	AVIStreamReadFormat(m_VideoStream,0,&bmpih,&VideoStreamSize);
	//error handling
	if(BI_RGB!=bmpih.biCompression){
		//error handling
		printf("Incorrect format of input avi file. Use uncoded video file.");
		exit(-1);
	}
	m_uWidth=bmpih.biWidth;
	m_uHeight=bmpih.biHeight;
	m_uScale=VideoStreamInfo.dwScale;
	m_uRate=VideoStreamInfo.dwRate;
	m_uLength=VideoStreamInfo.dwLength;
	m_uFrameSize=bmpih.biSizeImage;
//	m_pFrameBGRData=new byte[bmpih.biSizeImage];
	//error handling
}

void AviMovie::writeHeader(unsigned uWidth,
							unsigned uHeight,
							unsigned uScale,
							unsigned uRate,
							unsigned uLength){
	AVISTREAMINFO VideoStreamInfo;
	BITMAPINFOHEADER bmpih;

	memset(&VideoStreamInfo,0,sizeof(VideoStreamInfo)); 
	VideoStreamInfo.fccType=streamtypeVIDEO;
	VideoStreamInfo.fccHandler=comptypeDIB;//uncompressed
	m_uScale=VideoStreamInfo.dwScale=uScale;
	m_uRate=VideoStreamInfo.dwRate=uRate;
	m_uLength=VideoStreamInfo.dwLength=uLength;
	VideoStreamInfo.dwQuality=0xffffffff; //default quality
	VideoStreamInfo.rcFrame.right=uWidth;
	VideoStreamInfo.rcFrame.bottom=uHeight;

	if(AVIFileCreateStream(m_aviFile,&m_VideoStream,&VideoStreamInfo)!=AVIERR_OK){
		//error handling
		exit(-1);
	}
	
	memset(&bmpih,0,sizeof(bmpih));
	bmpih.biSize=40;
	m_uWidth =bmpih.biWidth=uWidth;
	m_uHeight=bmpih.biHeight=uHeight;
	bmpih.biPlanes=1;
	bmpih.biBitCount=24;
	bmpih.biCompression=BI_RGB;
	bmpih.biXPelsPerMeter=2834;
	bmpih.biYPelsPerMeter=2834;
	m_uFrameSize=bmpih.biSizeImage=((uWidth*3+3)&0xFFFFFFFC)*uHeight;

	AVIStreamSetFormat(m_VideoStream, 0, &bmpih, sizeof(bmpih));
}

void AviMovie::getNextFrameYUV(byte* pFrameY,byte* pFrameU,byte* pFrameV){	
	unsigned uAlignBytes;
	unsigned uByteWidth;

	AVIStreamRead(m_VideoStream,m_uCurrentFrameNum,1,m_pFrameContainer,m_uFrameSize,NULL,NULL);
	
	uAlignBytes=m_uWidth*3&0x00000003;
	if(0!=uAlignBytes) uAlignBytes=4-uAlignBytes;
	uByteWidth=m_uWidth*3;

	byte* pFrameUWhole=new byte[m_uPlaneSize];
	byte* pFrameVWhole=new byte[m_uPlaneSize];

	unsigned i,j;
	j=0;
	for(i=0;i<m_uFrameSize;i+=3){
		pFrameY[j]=round(0.299*m_pFrameContainer[i+2]+0.587*m_pFrameContainer[i+1]+0.114*m_pFrameContainer[i]);
		pFrameV[j]=round(0.5*m_pFrameContainer[i+2]-0.4187*m_pFrameContainer[i+1]-0.0813*m_pFrameContainer[i]+128);
		pFrameU[j]=round(-0.1687*m_pFrameContainer[i+2]-0.3313*m_pFrameContainer[i+1]+0.5*m_pFrameContainer[i]+128);
				
		if((i&uByteWidth)==uByteWidth) 
			i+=uAlignBytes;
		j++;
	}
	m_uCurrentFrameNum++;
}
void AviMovie::putNextFrameYUV(byte *pFrameY, byte *pFrameU, byte *pFrameV){
	unsigned i,j;
	unsigned uByteWidth;
	unsigned uAlignBytes;
	
	uByteWidth=m_uWidth*3;
	uAlignBytes=m_uWidth*3&0x00000003;
	if(0!=uAlignBytes) uAlignBytes=4-uAlignBytes;
	
	j=0;
	for(i=0; i<m_uFrameSize; i+=3){
		m_pFrameContainer[i]=round(pFrameY[j]+1.772*(pFrameU[j]-128));
		m_pFrameContainer[i+1]=round(pFrameY[j]-0.34414*(pFrameU[j]-128)-0.71414*(pFrameV[j]-128));
		m_pFrameContainer[i+2]=round(pFrameY[j]+1.402*(pFrameV[j]-128));
		
		if((i&uByteWidth)==uByteWidth) i+=uAlignBytes;
		j++;
	}
	if(AVIStreamWrite(m_VideoStream,m_uCurrentFrameNum,1,m_pFrameContainer,
				m_uFrameSize,AVIIF_KEYFRAME,NULL,NULL)!=AVIERR_OK){
		//error handling
		exit(-1);
	}	 

	m_uCurrentFrameNum++;
}	
inline byte AviMovie::round(double dValue){
	if(dValue>255) return 255;
	if(dValue<0)	return 0;
	return (byte)dValue;
}

unsigned AviMovie::getWidth(){
	return m_uWidth;
}
unsigned AviMovie::getHeight(){
	return m_uHeight;
}
unsigned AviMovie::getScale(){	
	return m_uScale;
}
unsigned AviMovie::getRate(){
	return m_uRate;
}
unsigned AviMovie::getLength(){
	return m_uLength;
}
unsigned AviMovie::getPlaneSize(){
	return m_uPlaneSize;
}